package com.atguigu.tingshu.account.service.impl;
import java.math.BigDecimal;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.mapper.RechargeInfoMapper;
import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.account.RechargeInfo;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Maps;

import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.model.account.UserAccount;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {

	@Autowired
	private UserAccountMapper userAccountMapper;

	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	@Autowired
	private UserAccountDetailMapper userAccountDetailMapper;

	@Autowired
	private RechargeInfoMapper rechargeInfoMapper;

	@Override
	public void initAccount(long userId) {
		UserAccount userAccount = new UserAccount();
		userAccount.setUserId(userId);
		userAccount.setTotalAmount(new BigDecimal("0"));
		userAccount.setLockAmount(new BigDecimal("0"));
		userAccount.setAvailableAmount(new BigDecimal("0"));
		userAccount.setTotalIncomeAmount(new BigDecimal("0"));
		userAccount.setTotalPayAmount(new BigDecimal("0"));

		//保存数据
		userAccountMapper.insert(userAccount);

	}

	@Override
	public BigDecimal getAvailableAmount() {
		Long userId = AuthContextHolder.getUserId();
		UserAccount userAccount = userAccountMapper.selectOne(new LambdaQueryWrapper<UserAccount>()
				.eq(UserAccount::getUserId, userId)
				.select(UserAccount::getAvailableAmount));
		return userAccount.getAvailableAmount();
	}

	@Override
	public AccountLockResultVo checkAndLock(AccountLockVo accountLockVo) {
		// 构建防止重复请求的redisKey，保证接口幂等性
		String idempotentKey = RedisConstant.CHECK_IDEMPOTENT + accountLockVo.getOrderNo() ;

		//构建检查锁定结果的数据key
		String lockKey = RedisConstant.ACCOUNT_LOCK_RESULT + accountLockVo.getOrderNo();

		//幂等性校验
		Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(idempotentKey, accountLockVo.getOrderNo(), 1, TimeUnit.HOURS);
		if(!aBoolean) {
			//从redis中获取锁定结果
			String accountLockResultJSON = stringRedisTemplate.opsForValue().get(lockKey);
			if(!StringUtils.isEmpty(accountLockResultJSON)){
				return JSON.parseObject(accountLockResultJSON, AccountLockResultVo.class);
			}else{ // 还未计算出结果就再次请求
				throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
			}
		}

		//检查可用金额
		UserAccount account = userAccountMapper.check(accountLockVo.getUserId(),accountLockVo.getAmount());
		if(account == null){ //可用金额不够
			stringRedisTemplate	.delete(idempotentKey);
			throw new GuiguException(ResultCodeEnum.ACCOUNT_LESS);
		}
		//	锁定账户金额
		int count = userAccountMapper.lock(accountLockVo.getUserId(),accountLockVo.getAmount());
		if(count == 0){ // 锁定失败
			stringRedisTemplate	.delete(idempotentKey);
			throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
		}
		//封装数据
		AccountLockResultVo accountLockResultVo = new AccountLockResultVo();
		accountLockResultVo.setUserId(accountLockVo.getUserId());
		accountLockResultVo.setAmount(accountLockVo.getAmount());
		accountLockResultVo.setContent(accountLockVo.getContent());

		this.log(accountLockVo.getUserId(),"锁定:" + accountLockVo.getAmount(),
				SystemConstant.ACCOUNT_TRADE_TYPE_LOCK,
				accountLockVo.getAmount(),accountLockVo.getOrderNo());

		//保存锁定结果
		stringRedisTemplate.opsForValue().set(lockKey, JSON.toJSONString(accountLockResultVo),1,TimeUnit.HOURS);
		return accountLockResultVo;
	}

	@Override
	public void minus(String orderNo) {
		//考虑接口的幂等性处理
		String idempotentKey = RedisConstant.ACCOUNT_MINUS_IDEMPOTENT + orderNo;
		//数据设置失败，说明是重复提交
		Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(idempotentKey, orderNo, 1, TimeUnit.HOURS);
		if(!aBoolean)return;
		//根据订单编号从redis中获取数据
		String accountRedisKey = RedisConstant.ACCOUNT_LOCK_RESULT + orderNo;
		String accountLockResultJSON = stringRedisTemplate.opsForValue().get(accountRedisKey);
		AccountLockResultVo accountLockResultVo = JSON.parseObject(accountLockResultJSON, AccountLockResultVo.class);
		//更改表中相关字段
		int count = userAccountMapper.minus(accountLockResultVo.getUserId(),accountLockResultVo.getAmount());
		if(count == 0){
			stringRedisTemplate.delete(idempotentKey);
			throw new GuiguException(ResultCodeEnum.ACCOUNT_MINUSLOCK_ERROR);
		}

		//记录账户变更日志
		this.log(accountLockResultVo.getUserId(),
				"扣减:" + accountLockResultVo.getContent() ,
				SystemConstant.ACCOUNT_TRADE_TYPE_MINUS,
				accountLockResultVo.getAmount(),orderNo);
		//删除redis中的数据
		stringRedisTemplate.delete(accountRedisKey);
	}

	@Override
	public void unlock(String orderNo) {
		//考虑接口的幂等性处理
		String idempotentKey = RedisConstant.ACCOUNT_UNLOCK_IDEMPOTENT + orderNo ;
		Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(idempotentKey, orderNo, 1, TimeUnit.HOURS);
		if(!aBoolean)return;
		//根据订单编号从redis中获取数据
		String accountRedisKey = RedisConstant.ACCOUNT_LOCK_RESULT + orderNo;
		String accountLockResultJSON = stringRedisTemplate.opsForValue().get(accountRedisKey);
		if(!StringUtils.isEmpty(accountLockResultJSON))return;
		AccountLockResultVo accountLockResultVo = JSON.parseObject(accountLockResultJSON, AccountLockResultVo.class);
		int count = userAccountMapper.unlock(accountLockResultVo.getUserId(),accountLockResultVo.getAmount());
		if(count == 0){
			stringRedisTemplate.delete(idempotentKey);
			throw new GuiguException(ResultCodeEnum.ACCOUNT_UNLOCK_ERROR);
		}

		this.log(accountLockResultVo.getUserId(),
				"解锁:" + accountLockResultVo.getContent() ,
				SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK,
				accountLockResultVo.getAmount(),orderNo);

		//删除redis中的数据
		stringRedisTemplate.delete(accountRedisKey);


	}

	@Override
	public void rechargePaySuccess(String orderNo) {
		LambdaQueryWrapper<RechargeInfo> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(RechargeInfo::getOrderNo,orderNo);
		RechargeInfo rechargeInfo = rechargeInfoMapper.selectOne(queryWrapper);
		if (SystemConstant.ORDER_STATUS_PAID.equals(rechargeInfo.getRechargeStatus())) return ;
		rechargeInfo.setRechargeStatus(SystemConstant.PAYMENT_STATUS_PAID);
		//更新充值订单状态
		rechargeInfoMapper.updateById(rechargeInfo);

		//更改账户金额
		Long userId = rechargeInfo.getUserId();
		LambdaQueryWrapper<UserAccount> userAccountLambdaQueryWrapper = new LambdaQueryWrapper<>();
		userAccountLambdaQueryWrapper.eq(UserAccount::getUserId,userId);
		UserAccount userAccount = userAccountMapper.selectOne(userAccountLambdaQueryWrapper);
		userAccount.setTotalAmount(userAccount.getTotalAmount().add(rechargeInfo.getRechargeAmount()));
		userAccount.setAvailableAmount(userAccount.getAvailableAmount().add(rechargeInfo.getRechargeAmount()));
		userAccountMapper.updateById(userAccount);

		this.log(userId,"充值:" + rechargeInfo.getRechargeAmount(),
				SystemConstant.ACCOUNT_TRADE_TYPE_DEPOSIT,
				rechargeInfo.getRechargeAmount(),
				orderNo);
	}

	@Override
	public IPage<UserAccountDetail> findUserRechargePage(Long pageNo, Long pageSize) {
		//获取用户id
		Long userId = AuthContextHolder.getUserId();
		IPage<UserAccountDetail> page = new Page<>(pageNo,pageSize);
		LambdaQueryWrapper<UserAccountDetail> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(UserAccountDetail::getUserId,userId);
		queryWrapper.eq(UserAccountDetail::getTradeType,SystemConstant.ACCOUNT_TRADE_TYPE_DEPOSIT);
		userAccountDetailMapper.selectPage(page,queryWrapper);
		return page;
	}

	@Override
	public IPage<UserAccountDetail> findUserConsumePage(Long pageNo, Long pageSize) {
		//获取用户id
		Long userId = AuthContextHolder.getUserId();
		IPage<UserAccountDetail> page = new Page<>(pageNo,pageSize);
		LambdaQueryWrapper<UserAccountDetail> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(UserAccountDetail::getUserId,userId);
		queryWrapper.eq(UserAccountDetail::getTradeType,SystemConstant.ACCOUNT_TRADE_TYPE_MINUS);
		userAccountDetailMapper.selectPage(page,queryWrapper);
		return page;
	}

	private void log(Long userId, String title, String tradeType, BigDecimal amount, String orderNo) {
		//构建账户明细日志对象
		UserAccountDetail userAccountDetail = new UserAccountDetail();
		userAccountDetail.setUserId(userId);
		userAccountDetail.setTitle(title);
		userAccountDetail.setTradeType(tradeType);
		userAccountDetail.setAmount(amount);
		userAccountDetail.setOrderNo(orderNo);

		//保存数据
		userAccountDetailMapper.insert(userAccountDetail);


	}
}
